package com.hmall.search.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldSort;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import com.hmall.common.domain.PageDTO;
import com.hmall.search.domain.po.ItemDoc;
import com.hmall.search.domain.query.ItemPageQuery;
import com.hmall.search.service.ISearchService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


@Service
@Slf4j
@AllArgsConstructor
public class SearchServiceImpl implements ISearchService {
    private final ElasticsearchClient esClient;
    @Override
    public PageDTO<ItemDoc> search(ItemPageQuery query) throws IOException {
        SearchResponse<ItemDoc> response = esClient.search(builder->builder
                        .index("items")
                        //查询条件
                        .query(qb->qb.bool(bb->{
                            //添加must主搜索条件
                            if (StrUtil.isNotBlank(query.getKey())) {
                                bb.must(mb -> mb.match(mqb -> mqb.field("name").query(query.getKey())));
                            }
                            //添加filter过滤条件-分类
                            if (StrUtil.isNotBlank(query.getCategory())) {
                                bb.filter(fb->fb.term(tb->tb.field("category").value(query.getCategory())));
                            }
                            //添加filter过滤条件-品牌
                            if (StrUtil.isNotBlank(query.getBrand())) {
                                bb.filter(fb -> fb.term(tb -> tb.field("brand").value(query.getBrand())));
                            }
                            //添加filter过滤条件-yij
                            if (ObjectUtil.isNotNull(query.getMinPrice()) && ObjectUtil.isNotNull(query.getMaxPrice())) {
                                bb.filter(fb -> fb.range(rb->rb
                                        .field("price")
                                        .gte(JsonData.of(query.getMinPrice()))
                                        .lt(JsonData.of(query.getMaxPrice()))));
                            }
                            return bb;
                        }))
                        //排序条件
                        .sort(sob->sob.field(fsb->{
                            if (StrUtil.isBlank(query.getSortBy())) {
                                //如果参数里的sortBy是空，说明要按照默认的关联度降序排列
                                fsb.field("_score").order(SortOrder.Desc);
                            }else{
                                //否则要按照sortBy字段排序，排序规则在isAsc
                                fsb.field(query.getSortBy())
                                        .order( query.getIsAsc() ? SortOrder.Asc : SortOrder.Desc );
                            }
                            return fsb;
                        }))
                        //起始索引 = (页码-1) * 每页数量
                        .from( (query.getPageNo() -1) * query.getPageSize() )
                        //每页数量
                        .size(query.getPageSize())
                        //高亮条件
                        .highlight(hb->hb.fields("name", fb->fb
                                .preTags("<em style='color:red;'>")
                                .postTags("</em>"))),
                ItemDoc.class);

        //构造返回值
        PageDTO<ItemDoc> pageDTO = new PageDTO<>();
        //  总数量
        pageDTO.setTotal(response.hits().total().value());
        //  数据列表
        List<ItemDoc> list = new ArrayList<>();
        List<Hit<ItemDoc>> hits = response.hits().hits();
        for (Hit<ItemDoc> hit : hits) {
            //先取原始文档对象
            ItemDoc doc = hit.source();
            //再取高亮结果。 为了程序的健壮性(鲁棒性)，所以加了判空
            Map<String, List<String>> map = hit.highlight();
            if (ObjectUtil.isNotEmpty(map)) {
                List<String> nameValues = map.get("name");
                if (ObjectUtil.isNotEmpty(nameValues)) {
                    doc.setName(nameValues.get(0));
                }
            }
            //添加到集合中
            list.add(doc);
        }
        pageDTO.setList(list);
        return pageDTO;
    }
}
